using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using NodeCanvas.Variables;
using UnityEngine;

namespace NodeCanvas
{
	[ExecuteInEditMode]
	[AddComponentMenu("NodeCanvas/Blackboard")]
	public class Blackboard : MonoBehaviour, ISavable
	{
		[Serializable]
		private class SerializedData
		{
			public string name;

			public Type type;

			public object value;

			public SerializedData(string name, Type type, object value)
			{
				this.name = name;
				this.type = type;
				this.value = value;
			}
		}

		[SerializeField]
		private string _blackboardName = string.Empty;

		[SerializeField]
		private List<VariableData> variables = new List<VariableData>();

		[SerializeField]
		private bool _isGlobal;

		private static Dictionary<string, Blackboard> _allBlackboards;

		public static Dictionary<string, Blackboard> allBlackboards
		{
			get
			{
				if (_allBlackboards == null)
				{
					_allBlackboards = new Dictionary<string, Blackboard>();
					Blackboard[] array = UnityEngine.Object.FindObjectsOfType<Blackboard>();
					foreach (Blackboard blackboard in array)
					{
						_allBlackboards[blackboard.blackboardName] = blackboard;
					}
				}
				return _allBlackboards;
			}
			private set
			{
				_allBlackboards = value;
			}
		}

		public string blackboardName
		{
			get
			{
				return _blackboardName;
			}
			set
			{
				if (string.IsNullOrEmpty(value))
				{
					value = base.gameObject.name + "_BB";
				}
				if (_blackboardName != value)
				{
					allBlackboards.Remove(_blackboardName);
					_blackboardName = value;
					allBlackboards[_blackboardName] = this;
				}
			}
		}

		public bool isGlobal
		{
			get
			{
				return _isGlobal;
			}
			set
			{
				_isGlobal = value;
			}
		}

		public string saveKey
		{
			get
			{
				return "Blackboard-" + blackboardName;
			}
		}

		public List<VariableData> GetAllData()
		{
			return new List<VariableData>(variables);
		}

		private VariableData AddData(string name, object value)
		{
			if (value == null)
			{
				return null;
			}
			VariableData variableData = AddData(name, value.GetType());
			if (variableData != null)
			{
				variableData.objectValue = value;
			}
			return variableData;
		}

		private VariableData AddData(string name, Type type)
		{
			VariableData variableData = null;
			Dictionary<Type, Type> typeDataRelation = GetTypeDataRelation();
			foreach (KeyValuePair<Type, Type> item in typeDataRelation)
			{
				if (item.Value == type || (item.Value.NCIsAssignableFrom(type) && !typeDataRelation.Values.ToList().Contains(type)))
				{
					variableData = (VariableData)base.gameObject.AddComponent(item.Key);
					break;
				}
			}
			if (variableData == null)
			{
				variableData = base.gameObject.AddComponent<SystemObjectData>();
			}
			variableData.dataName = name;
			variables.Add(variableData);
			variableData.hideFlags = HideFlags.HideInInspector;
			return variableData;
		}

		private Dictionary<Type, Type> GetTypeDataRelation()
		{
			Dictionary<Type, Type> dictionary = new Dictionary<Type, Type>();
			foreach (Type assemblyType in NCReflection.GetAssemblyTypes())
			{
				if (typeof(VariableData).NCIsAssignableFrom(assemblyType) && !assemblyType.NCIsAbstract() && assemblyType.NCGetAttribute(typeof(ObsoleteAttribute), true) == null)
				{
					FieldInfo fieldInfo = assemblyType.NCGetField("value");
					if (fieldInfo != null)
					{
						dictionary[assemblyType] = fieldInfo.FieldType;
					}
				}
			}
			return dictionary;
		}

		public VariableData SetDataValue(string name, object value)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			VariableData data = GetData(name, (value == null) ? typeof(object) : value.GetType());
			if (data != null)
			{
				data.objectValue = value;
			}
			else if (value != null)
			{
				Debug.Log("No VariableData of name '" + name + "' and type '" + value.GetType().Name + "' exists. Adding new instead...");
				return AddData(name, value);
			}
			return data;
		}

		public T GetDataValue<T>(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return default(T);
			}
			VariableData data = GetData(name, typeof(T));
			if (data == null || data.objectValue == null || data.objectValue.Equals(null))
			{
				return default(T);
			}
			return (T)data.objectValue;
		}

		public object GetDataValue(string name, Type type)
		{
			VariableData data = GetData(name, type);
			return (!(data != null)) ? null : data.objectValue;
		}

		public bool HasData<T>(string dataName)
		{
			return GetData(dataName, typeof(T)) != null;
		}

		public void DeleteData(string dataName)
		{
			VariableData data = GetData(dataName, typeof(object));
			if (data != null)
			{
				variables.Remove(data);
				UnityEngine.Object.DestroyImmediate(data, true);
			}
		}

		public VariableData GetData(string dataName, Type ofType)
		{
			VariableData result = null;
			foreach (VariableData variable in variables)
			{
				if (variable.dataName == dataName)
				{
					if (ofType == null || variable.varType == ofType || ofType.NCIsAssignableFrom(variable.varType))
					{
						return variable;
					}
					if (variable.varType.NCIsAssignableFrom(ofType))
					{
						result = variable;
					}
				}
			}
			return result;
		}

		public string[] GetDataNames()
		{
			string[] array = new string[variables.Count];
			for (int i = 0; i < variables.Count; i++)
			{
				array[i] = variables[i].dataName;
			}
			return array;
		}

		public string[] GetDataNames(Type ofType)
		{
			List<string> list = new List<string>();
			foreach (VariableData variable in variables)
			{
				if (ofType.NCIsAssignableFrom(variable.varType))
				{
					list.Add(variable.dataName);
				}
			}
			return list.ToArray();
		}

		public static Blackboard Find(string bbName)
		{
			if (allBlackboards.ContainsKey(bbName))
			{
				return allBlackboards[bbName];
			}
			return null;
		}

		private void Awake()
		{
			if (isGlobal && allBlackboards.ContainsKey(blackboardName) && allBlackboards[blackboardName].isGlobal)
			{
				Debug.LogWarning("More than one Global Blackboards exist with the same name '" + blackboardName + "'. Make sure they have different names", base.gameObject);
			}
			allBlackboards[blackboardName] = this;
		}

		private void OnDestroy()
		{
			allBlackboards.Remove(blackboardName);
			foreach (VariableData variable in variables)
			{
				UnityEngine.Object.DestroyImmediate(variable);
			}
		}

		public string Save()
		{
			if (!Application.isPlaying)
			{
				Debug.Log("You can only Save a blackboard in runtime for safety...");
				return null;
			}
			BinaryFormatter binaryFormatter = new BinaryFormatter();
			MemoryStream memoryStream = new MemoryStream();
			List<SerializedData> list = new List<SerializedData>();
			foreach (VariableData variable in variables)
			{
				object serialized = variable.GetSerialized();
				if (serialized == null || (serialized != null && serialized.GetType().IsSerializable))
				{
					list.Add(new SerializedData(variable.dataName, variable.GetType(), serialized));
					continue;
				}
				list.Add(new SerializedData(variable.dataName, variable.GetType(), null));
				Debug.LogWarning(string.Concat("Blackbaord '", variable.varType, "' data doesn't support save/load. '", variable.dataName, "'"));
			}
			binaryFormatter.Serialize(memoryStream, list);
			PlayerPrefs.SetString(saveKey, Convert.ToBase64String(memoryStream.GetBuffer()));
			Debug.Log("Saved: " + saveKey, base.gameObject);
			return saveKey;
		}

		public bool Load()
		{
			if (!Application.isPlaying)
			{
				Debug.Log("You can only Load a blackboard in runtime for safety...");
				return false;
			}
			string @string = PlayerPrefs.GetString(saveKey);
			if (@string == string.Empty)
			{
				Debug.Log("No Save found for: " + saveKey);
				return false;
			}
			foreach (VariableData variable in variables)
			{
				UnityEngine.Object.DestroyImmediate(variable, true);
			}
			variables.Clear();
			BinaryFormatter binaryFormatter = new BinaryFormatter();
			MemoryStream serializationStream = new MemoryStream(Convert.FromBase64String(@string));
			List<SerializedData> list = binaryFormatter.Deserialize(serializationStream) as List<SerializedData>;
			foreach (SerializedData item in list)
			{
				VariableData variableData = base.gameObject.AddComponent(item.type) as VariableData;
				variableData.hideFlags = HideFlags.HideInInspector;
				variableData.dataName = item.name;
				variableData.SetSerialized(item.value);
				variables.Add(variableData);
			}
			Debug.Log("Loaded: " + saveKey, base.gameObject);
			return true;
		}
	}
}
